﻿// ....................................................................................................................
//	Author: Copyright (C) 2012 Ing. Marek Hubal
//	Email: hubal@acasoftware.org
// ....................................................................................................................

using System;

namespace core
{
    /// <summary>Trieda IBinaryTextViewer</summary>
	public class IBinaryTextViewer : System.Windows.Forms.UserControl
	{
        /// <summary>Trieda IWonCanvas</summary>
        protected class IOwnCanvas : System.Windows.Forms.UserControl
        {
            /// <summary>Konstruktor</summary>
            public IOwnCanvas()
            {
                SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
                SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
                SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true);
            }
            protected override bool IsInputKey(System.Windows.Forms.Keys keyData)
            {
                if (keyData == System.Windows.Forms.Keys.Down) return true;
                if (keyData == System.Windows.Forms.Keys.Up) return true;
                return base.IsInputKey(keyData);
            }
        }

        /// <summary>Trieda IFindDataStr</summary>
        protected class IFindDataStr
        {
            public string FindValue; // Hodnota na najdenie
            public bool IgnoreCase; // Priznak ignorovania velkosti pismen
            public bool BackwardSearch; // Priznak hladania smerom dozadu

            /// <summary>Konstruktor</summary>
            public IFindDataStr()
            {
                FindValue = "";
                IgnoreCase = true;
                BackwardSearch = false;
            }
            /// <summary>Skopirovanie obsahu objektu</summary>
            /// <param name="pValue">objekt na skopirovanie</param>
            public void Assign(IFindDataStr pValue)
            {
                FindValue = pValue.FindValue;
                IgnoreCase = pValue.IgnoreCase;
                BackwardSearch = pValue.BackwardSearch;
            }

            /// <summary>Serializacia obsahu</summary>
            /// <returns>serializovany obsah</returns>
            public string Serialize()
            {
                using (IParamList L = new IParamList())
                {
                    L.SetParam("FV", FindValue);
                    L.SetParam("IC", IgnoreCase);
                    L.SetParam("BS", BackwardSearch);
                    return L.Serialize(true);
                }
            }
            /// <summary>Deserializacia obsahu</summary>
            /// <param name="pData">data</param>
            public void Deserialize(string pData)
            {
                using (IParamList L = new IParamList())
                {
                    L.Deserialize(pData, true);
                    FindValue = L.GetParam("FV", "");
                    IgnoreCase = L.GetParam("IC", true);
                    BackwardSearch = L.GetParam("BS", false);
                }
            }

            /// <summary>Editacia pomocou formluara</summary>
            /// <returns>true / fase</returns>
            public bool EditWithForm()
            {
                using (IFindForm_Edit F = new IFindForm_Edit()) // Vytvorim okno
                {
                    F.Value.Assign(this);
                    bool B = F.ShowDialog() == System.Windows.Forms.DialogResult.OK;
                    if (B) Assign(F.Value);
                    return B;
                }
            }
        }

        /// <summary>Trieda IFindForm_Edit</summary>
        protected class IFindForm_Edit : IForm
        {
            public IFindDataStr Value; // Objekt na editaciu

            protected IGroupBox FGrp; // Controls
            protected IButton FBtn_OK;
            protected IButton FBtn_Cancel;

            protected ILabel FLabel1;
            protected ITextBox FText_SearchValue;
            protected ICheckBox FCheck_IgnoreCase;
            protected ICheckBox FCheck_BackwardSearch;

            /// <summary>Konstruktor</summary>
            public IFindForm_Edit()
            {
                Value = new IFindDataStr();
            }

            /// <summary>Metoda: OnCreateForm</summary>
            /// <returns>true / false</returns>
            protected override bool OnCreateForm()
            {
                if (!base.OnCreateForm()) return false; // Volam predchodcu

                // Inicializacia
                MinimizeBox = false; MaximizeBox = false; FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; ShowIcon = true; ShowInTaskbar = false; StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;

                SetBounds(0, 0, 500, 200); System.Drawing.Size CS = ClientSize;

                Text = lang.FORM_VIEWER_TEXT_6;

                // Vytvorim groupbox
                FGrp = new IGroupBox(); IControl.PlaceControl_Inside(FGrp, this, 10, 10, 10, 50);

                FLabel1 = new ILabel(lang.FORM_VIEWER_TEXT_6_3);
                FText_SearchValue = new ITextBox();
                FCheck_IgnoreCase = new ICheckBox(lang.FORM_VIEWER_TEXT_6_4);
                FCheck_BackwardSearch = new ICheckBox(lang.FORM_VIEWER_TEXT_6_5);

                FGrp.Controls.AddRange(new System.Windows.Forms.Control[] { FLabel1, FText_SearchValue, FCheck_IgnoreCase, FCheck_BackwardSearch });
                IControl.PlaceControl_Inside_LeftTop_LeftTop(FLabel1, FGrp, 10, 20);
                IControl.PlaceControl_Outside_LeftTop_LeftBottom(FText_SearchValue, FLabel1, 0, 10, FGrp.ClientSize.Width - 20);
                IControl.PlaceControl_Outside_LeftTop_LeftBottom(FCheck_IgnoreCase, FText_SearchValue, 0, 10);
                IControl.PlaceControl_Outside_LeftTop_TopRight(FCheck_BackwardSearch, FCheck_IgnoreCase, 0, 20);

                // Vytvorim tlacidla
                FBtn_Cancel = new IButton(lang.FORM_VIEWER_TEXT_6_2); IControl.PlaceControl_Outside_TopRight_RightBottom(FBtn_Cancel, FGrp, 0, 10); FBtn_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                FBtn_OK = new IButton(lang.FORM_VIEWER_TEXT_6_1, Event_BtnOKClick); IControl.PlaceControl_Outside_TopRight_LeftTop(FBtn_OK, FBtn_Cancel, 0, 10);

                Controls.AddRange(new System.Windows.Forms.Control[] { FGrp, FBtn_OK, FBtn_Cancel }); // Zaradim do controls

                AcceptButton = FBtn_OK; CancelButton = FBtn_Cancel; FBtn_OK.Focus();

                FillForm(Value);
                return true; // Vsetko OK
            }

            /// <summary>Vyplnenie formulara</summary>
            /// <param name="pValue">objekt s hodnotami</param>
            protected void FillForm(IFindDataStr pValue)
            {
                FText_SearchValue.Text = pValue.FindValue;
                FCheck_IgnoreCase.Checked = pValue.IgnoreCase;
                FCheck_BackwardSearch.Checked = pValue.BackwardSearch;
            }
            /// <summary>Nacitanie obsahu formulara</summary>
            /// <param name="Result">vysledok</param>
            protected void ReadForm(IFindDataStr Result)
            {
                Result.FindValue = FText_SearchValue.Text;
                Result.IgnoreCase = FCheck_IgnoreCase.Checked;
                Result.BackwardSearch = FCheck_BackwardSearch.Checked;
            }
            /// <summary>Test validity dat formulara</summary>
            /// <returns>true / false</returns>
            protected bool CheckForm()
            {
                if (FText_SearchValue.Text == "") // Prazdna hodnota na hladanie?
                {
                    IApp.ShowError(lang.FORM_VIEWER_TEXT_6_6);
                    FText_SearchValue.Focus();
                    return false;
                }
                return true; // OK
            }

            /// <summary>Event: Click pre tlacidlo OK</summary>
            /// <param name="pSender">zdroj</param>
            /// <param name="e">parameter</param>
            protected void Event_BtnOKClick(object pSender, EventArgs e)
            {
                if (!CheckForm()) return;
                
                ReadForm(Value); // Nacitam data
                DialogResult = System.Windows.Forms.DialogResult.OK;
            }
        }

        public const long MAX_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD = 100 * 1024 * 1024;
        public const long WARNING_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD = 1 * 1024 * 1024;

        protected delegate void DelegateStopSearch();

        protected System.Collections.Generic.List<string> FFilesToView; // Zoznam suborov na prezeranie
        protected int FCurrentViewFileIndex; // Index aktualneho suboru
        protected IFileExplorerForm_Viewer.IViewerSettings FSettings; // Nastavenie
        protected IFileExplorerForm_Viewer FParent;

        protected System.IO.FileStream FCurrentFileStream; // Stream aktualneho suboru
        protected long FCurrentFileSize; // Dlzka aktualneho suboru

        protected IToolStrip FMainMenu; // Controls
        protected ISimpleStatusStrip FStatus;
        protected IOwnCanvas FPanel;
        protected System.Windows.Forms.VScrollBar FVScrollBar;
        protected IToolStripMenuItem FMI_DOSEncoding;
        protected IToolStripMenuItem FMI_ANSIEncoding;

        protected int FColsCount;
        protected long FRowsCount;
        protected int FColWidth, FRowHeight;
        protected int FWidth;
        protected long FHeight;
        protected long FFirstRow;
        protected byte[] FBuffer;
        protected long FMultiP = 1;
        protected System.Text.Encoding FCurrentEncoding;
        protected long FStartSelection, FEndSelection;
        protected long FMouseStartSelection;
        protected int FMouseMoveStateInfo;

        protected IFindDataStr FSearchData;
        protected bool FSearchRunning;
        protected System.Threading.Thread FSearchThread;
        protected bool FEscPressed;
        protected long FSearchPosition;
        protected long FSearchResultPosition;
        protected int FSearchResultLength;
        protected long FLastSearchResultPosition;
        protected bool FIsBusy;

        /// <summary>Dispose metoda</summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (FCurrentFileStream != null)
            {
                FCurrentFileStream.Close();
                FCurrentFileStream = (System.IO.FileStream)ICore.DisposeObject(FCurrentFileStream);
            }
            base.Dispose(disposing); // Volam predchodcu
        }

        /// <summary>Vytvorenie obsahu zo zadaneho suboru</summary>
        /// <param name="pFileIndex">subo</param>
        protected void CreateContent(int pFileIndex)
        {
            CloseCurrentContent(); // Uzatvorim aktualny obsah
            try
            {
                FCurrentFileStream = new System.IO.FileStream(FFilesToView[pFileIndex], System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite); // Otvorim subor
            }
            catch // Chyba
            {
                CloseCurrentContent();
                FParent.UpdateTitle(FFilesToView[pFileIndex], true); // Aktualizujem nadpis okna
                CreateMainMenu();
                return;
            }
            FCurrentViewFileIndex = pFileIndex;
            FCurrentFileSize = new System.IO.FileInfo(FFilesToView[pFileIndex]).Length; // Ziskam dlzku

            using (System.Drawing.Graphics G = FPanel.CreateGraphics()) // Urcim rozmery znaku
            {
                System.Drawing.SizeF S = G.MeasureString("X", FPanel.Font);
                FColWidth = (int)S.Width - 4; FRowHeight = (int)S.Height;
            }
            FColsCount = FSettings.Mode0_ColsCount; FRowsCount = (int)(FCurrentFileSize / FColsCount) + 1; // Urcim pocet stlpcov a riadkov
            FWidth = FColsCount * FColWidth; FHeight = FRowsCount * FRowHeight;
            FFirstRow = 0;

            FBuffer = new byte[FColsCount]; // Alokujem buffer

            UpdateScrollBarsDefs();
            FParent.UpdateTitle(FFilesToView[pFileIndex], false); // Aktualizujem nadpis okna
            CreateMainMenu(); // Vytvorim hlavne menu

            UpdateScrollBarsDefs(); // Aktualizujem scrollbar
            FVScrollBar.Value = 0;
            FPanel.Invalidate(); // Prekreslim obsah okna
        }
        /// <summary>Uzatvorenie aktualneho obsahu</summary>
        protected void CloseCurrentContent()
        {
            FCurrentFileStream = (System.IO.FileStream)ICore.DisposeObject(FCurrentFileStream);
            FRowsCount = FColsCount = 0; FWidth = 0; FHeight = 0;
            FStartSelection = FEndSelection = -1; FMouseStartSelection = -1;
        }
        /// <summary>Aktualizacia parametrov scrollbarov</summary>
        protected void UpdateScrollBarsDefs()
        {
            System.Drawing.Size CS = FPanel.ClientSize;
            if (FHeight < CS.Height) FVScrollBar.Visible = false;
            else
            {
                FVScrollBar.Visible = true;
                FVScrollBar.Minimum = 0;
                FMultiP = 1;
                if (FRowsCount >= (long)int.MaxValue)
                {
                    long V = FRowsCount;
                    while (V >= (long)int.MaxValue)
                    {
                        FMultiP *= 10; V /= 10;
                    }
                }
                FVScrollBar.Maximum = (int)(FRowsCount / FMultiP);
                FVScrollBar.SmallChange = 1; FVScrollBar.LargeChange = ((CS.Height / FRowHeight) + 1) * (int)FMultiP;
                FVScrollBar.Enabled = true;
            }
        }
        /// <summary>Nacitanie bufra zo suboru</summary>
        /// <param name="pPosition">pozicia</param>
        /// <param name="pMaxLength">max. dlzka</param>
        /// <param name="Result">vysledok</param>
        /// <returns>pocet nacitanych bajtov</returns>
        protected int ReadFromFile(long pPosition, int pMaxLength, byte[] Result)
        {
            if (pPosition >= FCurrentFileSize) return 0; // Mimo rozsah?
            try
            {
                FCurrentFileStream.Position = pPosition;
                return FCurrentFileStream.Read(Result, 0, pMaxLength);
            }
            catch // Chyba
            {
                return 0;
            }
        }
        /// <summary>Vytvorenie hlavneho menu</summary>
        protected void CreateMainMenu()
        {
            IToolStripMenuItem M1;

            FMainMenu.Items.Clear(); // Zrusim aktualny obsah
            if (FCurrentFileStream == null) // Menu pre chybovy subor
            {
                M1 = IToolStrip.CreateMenuItem(FMainMenu.Items, lang.FORM_VIEWER_TEXT_2);
                M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_1, Event_MIClick_OpenFile).ShortcutKeys = System.Windows.Forms.Keys.O | System.Windows.Forms.Keys.Control;
                if (FFilesToView.Count > 1) // Je viac suborov?
                {
                    M1.CreateSeparator();
                    M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_2, Event_MIClick_NextFile).ShortcutKeys = System.Windows.Forms.Keys.Right | System.Windows.Forms.Keys.Control;
                    M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_3, Event_MIClick_PrevFile).ShortcutKeys = System.Windows.Forms.Keys.Left | System.Windows.Forms.Keys.Control;
                }
                M1.CreateSeparator();
                M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_5, Event_MIClick_Close);
                return;
            }

            M1 = IToolStrip.CreateMenuItem(FMainMenu.Items, lang.FORM_VIEWER_TEXT_2); // Subor
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_1, Event_MIClick_OpenFile).ShortcutKeys = System.Windows.Forms.Keys.O | System.Windows.Forms.Keys.Control;
            if (FFilesToView.Count > 1) // Je viac suborov?
            {
                M1.CreateSeparator();
                M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_2, Event_MIClick_NextFile).ShortcutKeys = System.Windows.Forms.Keys.Right | System.Windows.Forms.Keys.Control;
                M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_3, Event_MIClick_PrevFile).ShortcutKeys = System.Windows.Forms.Keys.Left | System.Windows.Forms.Keys.Control;
            }
            M1.CreateSeparator();
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_4, Event_MIClick_Reload);
            M1.CreateSeparator();
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_2_5, Event_MIClick_Close);

            M1 = IToolStrip.CreateMenuItem(FMainMenu.Items, lang.FORM_VIEWER_TEXT_4); // Oznacenie
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_4_1, Event_MIClick_SelectAll).ShortcutKeys = System.Windows.Forms.Keys.A | System.Windows.Forms.Keys.Control;
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_4_2, Event_MIClick_CopyToClipboard).ShortcutKeys = System.Windows.Forms.Keys.C | System.Windows.Forms.Keys.Control;
            M1.CreateSeparator();
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_4_3, Event_MIClick_Search).ShortcutKeys = System.Windows.Forms.Keys.F7;
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_4_4, Event_MIClick_SearchNext).ShortcutKeys = System.Windows.Forms.Keys.F3;

            M1 = IToolStrip.CreateMenuItem(FMainMenu.Items, lang.FORM_VIEWER_TEXT_3); // Zobrazenie
            FMI_ANSIEncoding = M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_3_1, Event_MIClick_ChangeEncodingToANSI); FMI_ANSIEncoding.Checked = FSettings.Mode0_Encoding == 1;
            FMI_DOSEncoding = M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_3_2, Event_MIClick_ChangeEncodingToDOS); FMI_DOSEncoding.Checked = FSettings.Mode0_Encoding == 0;
            M1.CreateSeparator();
            M1.CreateMenuItem(lang.FORM_VIEWER_TEXT_3_3, Event_MIClick_ChangeFont);
        }
        /// <summary>Aktualizacia statusu</summary>
        protected void UpdateStatus()
        {
            if (InvokeRequired)
            {
                Invoke(new DelegateStopSearch(UpdateStatus));
                return;
            }

            if (FSearchThread != null) // Vypis procesu hladania?
            {
                double D = Math.Round(100*((double)(FSearchPosition) / (double)(FCurrentFileSize)), 1);
                FStatus.SimpleText = string.Format(lang.FORM_VIEWER_TEXT_6_7, D);
                return;
            }

            // Obycajny vypis
            if (FCurrentFileStream == null) FStatus.SimpleText = "";
            else
            {
                string Sel = FStartSelection == -1 ? lang.FORM_VIEWER_TEXT_2_9 : string.Format(lang.FORM_VIEWER_TEXT_2_10, FStartSelection, FEndSelection - FStartSelection + 1);
                FStatus.SimpleText = string.Format(lang.FORM_VIEWER_TEXT_2_8, ICore.Size2String((ulong)(FFirstRow * FColsCount), 0), ICore.Size2String((ulong)FCurrentFileSize, 0), Sel);
            }
        }
        /// <summary>Vypocet oznacenia na danom riadku</summary>
        /// <param name="pPosition">pozicia</param>
        /// <param name="StartCol">zaciatocny stlpec</param>
        /// <param name="ColsCount">dlzka</param>
        /// <returns>true / false</returns>
        protected bool CalculateRowSelection(long pPosition, out int StartCol, out int ColsCount)
        {
            StartCol = -1; ColsCount = 0; // Inicializacia vystupu

            if (FStartSelection == -1) return false; 
            if (FEndSelection < pPosition) return false;
            long EndP = pPosition + FColsCount - 1;
            if (EndP < FStartSelection) return false;

            if (FStartSelection <= pPosition)
            {
                if (FEndSelection > EndP)
                {
                    StartCol = 0; ColsCount = FColsCount;
                }
                else
                {
                    StartCol = 0;
                    ColsCount = (int)(FEndSelection - pPosition) + 1;
                }
            }
            else
            {
                if (FEndSelection > EndP)
                {
                    StartCol = (int)(FStartSelection - pPosition);
                    ColsCount = (int)(FColsCount - StartCol) + 1;
                }
                else
                {
                    StartCol = (int)(FStartSelection - pPosition);
                    ColsCount = (int)(FEndSelection - FStartSelection) + 1;
                }
            }

            return true;
        }
        /// <summary>Ziskanie pozicie znaku pod kurzorom</summary>
        /// <param name="pY">Y pozicia</param>
        /// <param name="pX">X pozicia</param>
        /// <returns>absolutna pozicia</returns>
        protected long GetCharPosition(int pY, int pX)
        {
            int R = pY / FRowHeight;
            int C = (pX - 6) / FColWidth;
            if (C < 0) C = 0;
            if (C >= FColsCount) C = FColsCount - 1;
            return (FFirstRow * FColsCount) + (long)(R * FColsCount) + (long)C;
        }
        /// <summary>Spustenie hladania</summary>
        protected void RunSearch()
        {
            FSearchThread = new System.Threading.Thread(delegate()
            {
                try
                {
                    long StartSearchPos = FLastSearchResultPosition != -1 ? FLastSearchResultPosition : FFirstRow * FColsCount;
                    if (StartSearchPos == FLastSearchResultPosition) StartSearchPos = FSearchData.BackwardSearch ? StartSearchPos - 1 : StartSearchPos + 1;
                    FSearchResultPosition = -1; FSearchResultLength = 0;
                    UpdateStatus(); // Aktualizujem status na zaciatku hladania

                    byte[] LowerCaseData = FCurrentEncoding.GetBytes(FSearchData.FindValue.ToLower());
                    byte[] UpperCaseData = FCurrentEncoding.GetBytes(FSearchData.FindValue.ToUpper());
                    byte[] NormalCaseData = FCurrentEncoding.GetBytes(FSearchData.FindValue);

                    int InputL;
                    if (FSearchData.IgnoreCase)  // Urcim dlzku vstupu
                    {
                        InputL = LowerCaseData.Length;
                        if (InputL > UpperCaseData.Length) InputL = UpperCaseData.Length;
                    }
                    else InputL = NormalCaseData.Length;
                    
                    byte[] Buffer = new byte[65536]; // Buffer pre data zo suboru
                    int BufferLen = -1; int BufferS = 0;

                    long Counter = FCurrentFileSize / 1000;
                    if (Counter < 1024 * 1024) Counter = 1024 * 1024;
                    long C = 0;

                    if (FSearchData.BackwardSearch) // Hladanie vzad
                    {
                        for (FSearchPosition = StartSearchPos - 1; FSearchPosition >= 0; FSearchPosition--) // Prehladavam do zaciatku
                        {
                            if (FEscPressed) throw new Exception();

                            if (BufferLen < InputL)
                            {
                                long P = FSearchPosition - Buffer.Length + InputL; if (P < 0) P = 0;
                                BufferLen = ReadFromFile(P, Buffer.Length, Buffer);
                                BufferS = (int)(FSearchPosition - P);
                                if (BufferLen < InputL) break;
                            }

                            int i;
                            if (FSearchData.IgnoreCase) // Bez ohladu na velkost?
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if ((Buffer[i + BufferS] != LowerCaseData[i]) && (Buffer[i + BufferS] != UpperCaseData[i])) break;
                                }
                            }
                            else
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if (Buffer[i + BufferS] != NormalCaseData[i]) break;
                                }
                            }
                            if (i == InputL)
                            {
                                FLastSearchResultPosition = FSearchResultPosition = FSearchPosition; FSearchResultLength = InputL;
                                throw new Exception();
                            }
                            BufferS--; BufferLen--;

                            if (C++ > Counter)
                            {
                                UpdateStatus();
                                C = 0;
                            }
                        }

                        BufferLen = -1; // Vynulujem buffer

                        for (FSearchPosition = FCurrentFileSize - 1; FSearchPosition >= StartSearchPos; FSearchPosition--) // Prehladavam od konca
                        {
                            if (FEscPressed) throw new Exception();

                            if (BufferLen < InputL)
                            {
                                long P = FSearchPosition - Buffer.Length + InputL; if (P < 0) P = 0;
                                BufferLen = ReadFromFile(P, Buffer.Length, Buffer);
                                BufferS = (int)(FSearchPosition - P);
                                if (BufferLen < InputL) break;
                            }

                            int i;
                            if (FSearchData.IgnoreCase) // Bez ohladu na velkost?
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if ((Buffer[i + BufferS] != LowerCaseData[i]) && (Buffer[i + BufferS] != UpperCaseData[i])) break;
                                }
                            }
                            else
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if (Buffer[i + BufferS] != NormalCaseData[i]) break;
                                }
                            }
                            if (i == InputL)
                            {
                                FLastSearchResultPosition = FSearchResultPosition = FSearchPosition; FSearchResultLength = InputL;
                                throw new Exception();
                            }
                            BufferS--; BufferLen--;

                            if (C++ > Counter)
                            {
                                UpdateStatus();
                                C = 0;
                            }
                        }
                    }
                    else // Dopredne hladanie
                    {
                        for (FSearchPosition = StartSearchPos; FSearchPosition < FCurrentFileSize - InputL; FSearchPosition++) // Prehladavam do konca
                        {
                            if (FEscPressed) throw new Exception();

                            if (BufferLen < InputL)
                            {
                                BufferS = 0;
                                BufferLen = ReadFromFile(FSearchPosition, Buffer.Length, Buffer);
                                if (BufferLen < InputL) break;
                            }

                            int i;
                            if (FSearchData.IgnoreCase) // Bez ohladu na velkost?
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if ((Buffer[i + BufferS] != LowerCaseData[i]) && (Buffer[i + BufferS] != UpperCaseData[i])) break;
                                }
                            }
                            else
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if (Buffer[i + BufferS] != NormalCaseData[i]) break;
                                }
                            }
                            if (i == InputL)
                            {
                                FLastSearchResultPosition = FSearchResultPosition = FSearchPosition; FSearchResultLength = InputL;
                                throw new Exception();
                            }
                            BufferS++; BufferLen--;

                            if (C++ > Counter)
                            {
                                UpdateStatus();
                                C = 0;
                            }
                        }

                        BufferLen = -1; // Vynulujem buffer

                        for (FSearchPosition = 0; FSearchPosition < StartSearchPos; FSearchPosition++) // Prehladavam od zaciatku
                        {
                            if (FEscPressed) throw new Exception();

                            if (BufferLen < InputL)
                            {
                                BufferS = 0;
                                BufferLen = ReadFromFile(FSearchPosition, Buffer.Length, Buffer);
                                if (BufferLen < InputL) break;
                            }

                            int i;
                            if (FSearchData.IgnoreCase) // Bez ohladu na velkost?
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if ((Buffer[i + BufferS] != LowerCaseData[i]) && (Buffer[i + BufferS] != UpperCaseData[i])) break;
                                }
                            }
                            else
                            {
                                for (i = 0; i < InputL; i++)
                                {
                                    if (Buffer[i + BufferS] != NormalCaseData[i]) break;
                                }
                            }
                            if (i == InputL)
                            {
                                FLastSearchResultPosition = FSearchResultPosition = FSearchPosition; FSearchResultLength = InputL;
                                throw new Exception();
                            }
                            BufferS++; BufferLen--;

                            if (C++ > Counter)
                            {
                                UpdateStatus();
                                C = 0;
                            }

                            if (C++ > Counter)
                            {
                                UpdateStatus();
                                C = 0;
                            }
                        }
                    }

                }
                catch
                {
                    FSearchThread = null;
                }
                StopSearch();
            });

            FEscPressed = false;
            FSearchThread.Priority = System.Threading.ThreadPriority.Lowest;
            FSearchThread.Start();

            if (FVScrollBar.Visible) FVScrollBar.Enabled = false;
            FMainMenu.Enabled = false;
            FSearchRunning = true;
            FIsBusy = true;
        }
        /// <summary>Zastavenie hladania</summary>
        protected void StopSearch()
        {
            if (InvokeRequired)
            {
                Invoke(new DelegateStopSearch(StopSearch));
                return;
            }

            if (FSearchThread != null)
            {
                FSearchThread = null;
                System.Threading.Thread.Sleep(200); // Pockam na ukoncenie
            }
            FSearchRunning = false;
            FMainMenu.Enabled = true;
            if (FVScrollBar.Visible) FVScrollBar.Enabled = true;

            if (FSearchResultPosition != -1) // Naslo sa nieco?
            {
                FStartSelection = FSearchResultPosition; FEndSelection = FStartSelection + FSearchResultLength - 1;
                FFirstRow = FStartSelection / FColsCount; // Urcim zaciatok
                
                int RowsPerPage = FPanel.ClientSize.Height / FRowHeight;
                long TotalRows = FCurrentFileSize / FColsCount;

                if (FFirstRow > TotalRows - RowsPerPage) FFirstRow = TotalRows - RowsPerPage;
                if (FFirstRow < RowsPerPage) FFirstRow = 0; // Urcim prvy riadok
                
                if (FVScrollBar.Visible) // Je scroll bar viditelny?
                {
                    if (FFirstRow > 0) FVScrollBar.Value = (int)(FFirstRow / FMultiP);
                }
                FPanel.Invalidate();
            }

            UpdateStatus();
            FIsBusy = false;
        }

        /// <summary>Event: Paint</summary>
        /// <param name="sender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            if (FRowsCount == 0) // Ziaden obsah?
            {
                using (System.Drawing.SolidBrush B = new System.Drawing.SolidBrush(System.Drawing.Color.White))
                {
                    e.Graphics.FillRectangle(B, FPanel.ClientRectangle);
                }
                UpdateStatus(); // Aktualizujem status
                return;
            }

            System.Drawing.Rectangle R = new System.Drawing.Rectangle();
            long Position = (long)FFirstRow * (long)FColsCount;
            long CurrRow = FFirstRow;
            int Top = 0;
            int SelStart, SelL;
            int Indent = 6;

            using (System.Drawing.StringFormat F = new System.Drawing.StringFormat(System.Drawing.StringFormatFlags.NoWrap | System.Drawing.StringFormatFlags.NoClip)) {
                F.Trimming = System.Drawing.StringTrimming.None; F.Alignment = System.Drawing.StringAlignment.Near; F.LineAlignment = System.Drawing.StringAlignment.Near;
                using (System.Drawing.SolidBrush BackB = new System.Drawing.SolidBrush(System.Drawing.Color.White))
                {
                    using (System.Drawing.SolidBrush SelB = new System.Drawing.SolidBrush(System.Drawing.Color.Silver))
                    {
                        using (System.Drawing.SolidBrush B = new System.Drawing.SolidBrush(ForeColor))
                        {
                            while (true)
                            {
                                if (CurrRow >= FRowsCount) break; // Koniec
                                if (Top >= e.ClipRectangle.Bottom) break;
                                int Len = ReadFromFile(Position, FColsCount, FBuffer);
                                if (Len == 0) break; // Koniec dat?

                                for (int i = 0; i < Len; i++)
                                {
                                    if (FBuffer[i] <= 0x20) FBuffer[i] = (byte)'.';
                                }

                                if (CalculateRowSelection(Position, out SelStart, out SelL))
                                {
                                    if (SelStart > 0)
                                    {
                                        R.X = 0; R.Y = Top; R.Height = FRowHeight; R.Width = SelStart * FColWidth;
                                        e.Graphics.FillRectangle(BackB, R);
                                    }
                                    R.X = SelStart * FColWidth + Indent; R.Y = Top; R.Height = FRowHeight; R.Width = SelL * FColWidth;
                                    e.Graphics.FillRectangle(SelB, R);

                                    if (SelStart + SelL < FColsCount)
                                    {
                                        R.X = (SelStart + SelL) * FColWidth + Indent; R.Y = Top; R.Height = FRowHeight; R.Width = (FColWidth - (SelStart + SelL)) * FColWidth;
                                        e.Graphics.FillRectangle(BackB, R);
                                    }

                                }
                                else
                                {
                                    R.X = 0; R.Y = Top; R.Height = FRowHeight; R.Width = e.ClipRectangle.Width;
                                    e.Graphics.FillRectangle(BackB, R);
                                }

                                string S = FCurrentEncoding.GetString(FBuffer, 0, Len);
                                for (int i = 0; i < Len; i++)
                                {
                                    e.Graphics.DrawString(S[i].ToString(), FPanel.Font, B, Indent + (i*FColWidth), (float)Top, F);
                                }
                                Top += FRowHeight; Position += FColsCount; CurrRow++;
                            }
                        }
                    }
                }
            }
            UpdateStatus(); // Aktualizujem status
        }
        /// <summary>Event: KeyDown</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_KeyDown(object pSender, System.Windows.Forms.KeyEventArgs e)
        {
            if (FSearchThread != null)
            {
                if (e.KeyCode == System.Windows.Forms.Keys.Escape)
                {
                    FEscPressed = true;
                }
                e.SuppressKeyPress = true;
                e.Handled = true;
                return;
            }

            switch (e.KeyCode)
            {
                case System.Windows.Forms.Keys.Escape:
                    {
                        FParent.Close();
                        e.SuppressKeyPress = true;
                        e.Handled = true;
                        return;
                    }
                case System.Windows.Forms.Keys.Down:
                case System.Windows.Forms.Keys.Up:
                case System.Windows.Forms.Keys.PageDown:
                case System.Windows.Forms.Keys.PageUp:
                    {
                        if (FVScrollBar.Visible)
                        {
                            IApp.SendKeyboardMessage(FVScrollBar, e.KeyCode);
                            e.SuppressKeyPress = true;
                            e.Handled = true;
                            return;
                        }
                    } break;
                case System.Windows.Forms.Keys.Home:
                    {
                        if ((e.Modifiers & System.Windows.Forms.Keys.Control) != 0)
                        {
                            if (FVScrollBar.Visible)
                            {
                                FVScrollBar.Value = 0;
                                e.SuppressKeyPress = true;
                                e.Handled = true;
                                return;
                            }
                        }
                    } break;
                case System.Windows.Forms.Keys.End:
                    {
                        if ((e.Modifiers & System.Windows.Forms.Keys.Control) != 0)
                        {
                            if (FVScrollBar.Visible)
                            {
                                FVScrollBar.Value = FVScrollBar.Maximum - FVScrollBar.LargeChange;
                                e.SuppressKeyPress = true;
                                e.Handled = true;
                                return;
                            }
                        }
                    } break;
            }
        }
        /// <summary>Event: Uzatvorenie rodicovskeho okna</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_Close(object pSender, EventArgs e)
        {
            FParent.Close(); // Uzatvorim rodica
        }
        /// <summary>Event: Znovuotvorenie aktualneho suboru</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_Reload(object pSender, EventArgs e)
        {
            CreateContent(FCurrentViewFileIndex); // Znovu otvorim subor
        }
        /// <summary>Event: Presun na nasledujuci subor</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_NextFile(object pSender, EventArgs e)
        {
            FCurrentViewFileIndex++;
            if (FCurrentViewFileIndex >= FFilesToView.Count) FCurrentViewFileIndex = 0;
            CreateContent(FCurrentViewFileIndex); // Nacitam novy subor
        }
        /// <summary>Event: Presun na predchadzajuci subor</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_PrevFile(object pSender, EventArgs e)
        {
            FCurrentViewFileIndex--;
            if (FCurrentViewFileIndex < 0) FCurrentViewFileIndex = FFilesToView.Count - 1;
            CreateContent(FCurrentViewFileIndex); // Nacitam novy subor
        }
        /// <summary>Event: Vyber suboru a otvorenie</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_OpenFile(object pSender, EventArgs e)
        {
            string FN = IApp.ShowEditor_ChooseFileLoad(lang.FORM_VIEWER_TEXT_2_6, lang.FORM_VIEWER_TEXT_2_7);
            if (string.IsNullOrEmpty(FN)) return; // Zrusena volba?
            FFilesToView.Clear(); FFilesToView.Add(FN); // Vymazem stary obsah a pridam novy subor
            CreateContent(0); // Otvorim ho
        }
        /// <summary>Event: Zmena kodovania na DOS</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_ChangeEncodingToDOS(object pSender, EventArgs e)
        {
            if (FSettings.Mode0_Encoding == 0) return; // Ziadna zmena?
            FSettings.Mode0_Encoding = 0;
            FCurrentEncoding = System.Text.Encoding.ASCII;
            FPanel.Invalidate();
            FMI_ANSIEncoding.Checked = false; FMI_DOSEncoding.Checked = true;
        }
        /// <summary>Event: Zmena kodovania na ANSI</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_ChangeEncodingToANSI(object pSender, EventArgs e)
        {
            if (FSettings.Mode0_Encoding == 1) return; // Ziadna zmena?
            FSettings.Mode0_Encoding = 1;
            FCurrentEncoding = System.Text.Encoding.Default;
            FPanel.Invalidate();
            FMI_ANSIEncoding.Checked = true; FMI_DOSEncoding.Checked = false;
        }
        /// <summary>Event: Oznacenie celeho obsahu</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_SelectAll(object pSender, EventArgs e)
        {
            FStartSelection = 0; FEndSelection = FCurrentFileSize - 1;
            FPanel.Invalidate();
        }
        /// <summary>Event: MouseDown</summary>
        /// <param name="sender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (FSearchThread != null) return; // Bezi hladanie?

            FStartSelection = FEndSelection = -1; // Zrusim aktualne oznacenie
            FPanel.Invalidate(); // Prekreslim

            FMouseStartSelection = GetCharPosition(e.Y, e.X);
            if (FMouseStartSelection > FCurrentFileSize)
            {
                FMouseStartSelection = -1;
                return;
            }
            FMouseMoveStateInfo = 0;
        }
        /// <summary>Event: MouseUp</summary>
        /// <param name="sender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (FSearchThread != null) return; // Bezi hladanie?
            FMouseStartSelection = -1;
        }
        /// <summary>Event: MouseMove</summary>
        /// <param name="sender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (FSearchThread != null) return; // Bezi hladanie?

            if (FMouseStartSelection == -1) return; // Nie je aktivne oznacovanie?
            if (FMouseMoveStateInfo != 0) return;

            if (e.Y > FPanel.ClientSize.Height) // Pohyb mysi smerom nadol mimo rozsah?
            {
                if (FVScrollBar.Visible)
                {
                    FMouseMoveStateInfo = 1;
                    IApp.SendKeyboardMessage(FVScrollBar, System.Windows.Forms.Keys.Down);
                    FMouseMoveStateInfo = 0;
                }
            }
            if (e.Y < 0) // Pohyb mysi smerom nahor mimo rozsah?
            {
                if (FVScrollBar.Visible)
                {
                    FMouseMoveStateInfo = 1;
                    IApp.SendKeyboardMessage(FVScrollBar, System.Windows.Forms.Keys.Up);
                    FMouseMoveStateInfo = 0;
                }
            }

            long Pos = GetCharPosition(e.Y, e.X);
            if (Pos >= FMouseStartSelection)
            {
                FStartSelection = FMouseStartSelection;
                FEndSelection = Pos;
                if (FEndSelection >= FCurrentFileSize) FEndSelection = FCurrentFileSize - 1;
            }
            else
            {
                FStartSelection = Pos;
                FEndSelection = FMouseStartSelection - 1;
                if (FStartSelection >= FCurrentFileSize)
                {
                    FStartSelection = FEndSelection = -1;
                }
            }
            FPanel.Invalidate();
        }
        /// <summary>Event: Skopirovanie oznacenia do schranky</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_CopyToClipboard(object pSender, EventArgs e)
        {
            if (FStartSelection == -1) return; // Ziadne oznacenie?
            long L = FEndSelection - FStartSelection + 1;
            if (L >= MAX_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD)
            {
                IApp.ShowError(string.Format(lang.FORM_VIEWER_TEXT_5, ICore.Size2String(MAX_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD, 1)));
                return;
            }
            if (L >= MAX_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD)
            {
                if (!IApp.ShowQuestion(string.Format(lang.FORM_VIEWER_TEXT_5_1, ICore.Size2String(WARNING_DATA_LENGTH_FOR_COPY_TO_CLIPBOPARD, 1)))) return;
            }

            try
            {
                IApp.App.WaitCursor = true;
                byte[] Buffer = new byte[L];
                long L1 = ReadFromFile(FStartSelection, (int)L, Buffer);
                for (long i = 0; i < L1; i++)
                {
                    if (Buffer[i] == 0) Buffer[i] = 0x1;
                }
                ICore.CopyTextToClipboard(FCurrentEncoding.GetString(Buffer, 0, (int)L1));
            }
            catch
            {
            }
            IApp.App.WaitCursor = false;
        }
        /// <summary>Event: Zobrazenie okna pre hladanie</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_Search(object pSender, EventArgs e)
        {
            FIsBusy = true;
            if (!FSearchData.EditWithForm())
            {
                FIsBusy = false;
                return;
            }
            FLastSearchResultPosition = -1;
            RunSearch();
        }
        /// <summary>Event: Zobrazenie okna pre dalsie hladanie</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_SearchNext(object pSender, EventArgs e)
        {
            if (FLastSearchResultPosition != -1) RunSearch();
        }
        /// <summary>Event: Zmena pisma</summary>
        /// <param name="pSender">zdroj</param>
        /// <param name="e">parameter</param>
        protected void Event_MIClick_ChangeFont(object pSender, EventArgs e)
        {
            FIsBusy = true;
            System.Drawing.Font F = new System.Drawing.Font(FPanel.Font, FPanel.Font.Style);
            if (IApp.ShowEditor_ChooseFont(ref F))
            {
                FPanel.Font = new System.Drawing.Font(F, F.Style);
                FSettings.Mode0_FontName = F.Name;
                FSettings.Mode0_FontSize = F.Size;
                FSettings.Mode0_FontStyle = (int)F.Style;
            }
            CreateContent(FCurrentViewFileIndex);
            F.Dispose();
            FIsBusy = false;
        }

        /// <summary>Konstruktor</summary>
        public IBinaryTextViewer(IFileExplorerForm_Viewer pParent, IFileExplorerForm_Viewer.IViewerSettings pSettings, System.Collections.Generic.List<string> pFiles, int pFileIndex)
        {
            FIsBusy = false;
            FParent = pParent;
            FSettings = pSettings;
            FFilesToView = new System.Collections.Generic.List<string>(); FFilesToView.AddRange(pFiles);
            FCurrentViewFileIndex = pFileIndex;
            FSearchData = new IFindDataStr(); FSearchRunning = false; FSearchData.Deserialize(FSettings.Mode0_FindData);

            switch (FSettings.Mode0_Encoding)
            {
                case 0: FCurrentEncoding = System.Text.Encoding.ASCII; break;
                default: FCurrentEncoding = System.Text.Encoding.Default; break;
            }

            FMainMenu = new IToolStrip(); FMainMenu.Dock = System.Windows.Forms.DockStyle.Top;
            FStatus = new ISimpleStatusStrip(); FStatus.Dock = System.Windows.Forms.DockStyle.Bottom;
            FPanel = new IOwnCanvas(); FPanel.Dock = System.Windows.Forms.DockStyle.Fill;
            FPanel.BackColor = System.Drawing.Color.White; FPanel.Font = new System.Drawing.Font(FSettings.Mode0_FontName, FSettings.Mode0_FontSize, (System.Drawing.FontStyle)FSettings.Mode0_FontStyle); FPanel.Cursor = System.Windows.Forms.Cursors.IBeam;
            FVScrollBar = new System.Windows.Forms.VScrollBar(); FVScrollBar.Dock = System.Windows.Forms.DockStyle.Right;

            Controls.AddRange(new System.Windows.Forms.Control[] { FPanel, FVScrollBar, FMainMenu, FStatus });
            FPanel.BringToFront();

            FPanel.Paint += Event_Paint;
            FPanel.KeyDown += Event_KeyDown;
            FPanel.MouseDown += Event_MouseDown;
            FPanel.MouseUp += Event_MouseUp;
            FPanel.MouseMove += Event_MouseMove;

            FPanel.Resize += (s, e) => // Lambda eventy
            {
                UpdateScrollBarsDefs();
            };
            FVScrollBar.ValueChanged += (s, e) => 
            {
                FFirstRow = FMultiP * FVScrollBar.Value;
                if (FMouseStartSelection != -1)
                {
                    System.Drawing.Point P = FPanel.PointToClient(MousePosition);
                    System.Windows.Forms.MouseEventArgs e1 = new System.Windows.Forms.MouseEventArgs(System.Windows.Forms.MouseButtons.Left, 0, P.X, P.Y, 0);
                    Event_MouseMove(FPanel, e1);
                }
                else FPanel.Invalidate();
            };
            FPanel.MouseWheel += (s, e) =>
            {
                if (FVScrollBar.Visible)
                {
                    int V;
                    if (e.Delta < 0) V = FVScrollBar.Value + FVScrollBar.LargeChange;
                    else V = FVScrollBar.Value - FVScrollBar.LargeChange;
                    if (V < 0) V = 0; // Korigujem aby hodnota sadla do pripustneho rozsahu
                    if (V >= FVScrollBar.Maximum - FVScrollBar.LargeChange) V = FVScrollBar.Maximum - FVScrollBar.LargeChange;
                    FVScrollBar.Value = V; // Ulozim novu hodnotu
                }
            };

            CreateContent(pFileIndex); // Vytvorim obsah
        }
        /// <summary>Ulozenie nastavenia</summary>
        public void SaveSettings(IFileExplorerForm_Viewer.IViewerSettings pSettings)
        {
            pSettings.Mode0_ColsCount = FSettings.Mode0_ColsCount;
            pSettings.Mode0_Encoding = FSettings.Mode0_Encoding;
            pSettings.Mode0_FontName = FSettings.Mode0_FontName;
            pSettings.Mode0_FontSize = FSettings.Mode0_FontSize;
            pSettings.Mode0_FontStyle = FSettings.Mode0_FontStyle;
            pSettings.Mode0_FindData = FSearchData.Serialize();
        }

        /// <summary>Property: IsBusy</summary>
        public bool IsBusy
        {
            get
            {
                return FIsBusy;
            }
        }
	}
}
